package com.adouteam.simple.wechat.tool;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import com.thoughtworks.xstream.io.xml.XppDomDriver;

import java.io.Writer;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * XStream工厂类
 *
 * @author buxianglong
 **/
public class XStreamFactory{
    private static String CDATA_PREFIX = "<![CDATA[";
    private static String CDATA_SUFFIX = "]]>";

    public static XStream getXStream(){
        final XmlFriendlyNameCoder nameCoder = new XmlFriendlyNameCoder("_-", "_");
        XStream xStream = new XStream(new XppDomDriver(nameCoder){
            @Override
            public HierarchicalStreamWriter createWriter(Writer out){
                return new PrettyPrintWriter(out, nameCoder){
                    boolean cdata = false;
                    List<Class<?>> targetClassList = new ArrayList<>();

                    @Override
                    public void startNode(String name, Class clazz){
                        super.startNode(name, clazz);
                        if(!targetClassList.contains(clazz)){
                            targetClassList.add(clazz);
                        }
                        cdata = needCDATA(targetClassList, name);
                    }

                    @Override
                    protected void writeText(QuickWriter writer, String text){
                        if(cdata){
                            writer.write(CDATA_PREFIX + text + CDATA_SUFFIX);
                        }else{
                            writer.write(text);
                        }
                    }
                };
            }
        });
        xStream.autodetectAnnotations(true);
        return xStream;
    }

    private static boolean needCDATA(List<Class<?>> targetClassList, String fieldAlias){
        for(Class targetClass : targetClassList){
            boolean cdata = existsCDATA(targetClass, fieldAlias);
            if(cdata){
                return cdata;
            }
            Class<?> superClass = targetClass.getSuperclass();
            while(!superClass.equals(Object.class)){
                cdata = existsCDATA(superClass, fieldAlias);
                if(cdata){
                    return cdata;
                }
                superClass = superClass.getSuperclass();
            }
        }
        return false;
    }

    private static boolean existsCDATA(Class<?> clazz, String fieldAlias){
        Field[] fields = clazz.getDeclaredFields();
        for(Field field : fields){
            if(field.getAnnotation(XStreamAnnotation.XStreamCDATA.class) != null){
                XStreamAlias xStreamAlias = field.getAnnotation(XStreamAlias.class);
                if(null != xStreamAlias){
                    if(fieldAlias.equals(xStreamAlias.value())){
                        return true;
                    }
                }else{
                    if(fieldAlias.equals(field.getName())){
                        return true;
                    }
                }
            }
        }
        return false;
    }
}
